extern crate pest;
#[macro_use]
extern crate pest_derive;

use pest::Parser;
use std::fs;
use std::path::Path;
use pest::iterators::Pair;


// Tell Rust, that the struct below shall be created from the grammar
// in the referenced file.
#[derive(Parser)]
#[grammar = "grammar.pest"]
pub struct CSVParser;

// The CSVParser has a procedure called `parse` at `CSVParser::parse`,
// which is able to recognize the grammar defined in the referenced
// file.

// Pest has also created an enum named `Rule` with a variant
// `Rule::field`.


fn read_file_to_string(path: &str) -> String {
    // Path::new("/etc/hosts").exists()
    let path = Path::new(path);
    fs::read_to_string(path).expect("cannot read file")
}

enum CSVValue {
    Number(f64),
}

fn parse_field(pair: Pair<Rule>) -> CSVValue {
    match pair.as_rule() {
        Rule::field => {
            let mut field_value: f64 = 0.0;
            for number in pair.into_inner() {
                match number.as_rule() {
                    Rule::number => {
                        // iterate over single fields
                        let number = number.as_str()
                            .parse::<f64>()
                            .expect(&format!("could not parse value as float64: {}",
                                             number.as_str()));
                        println!("parsed a number: {}", number);
                        field_value = number;
                    },
                    _ => {
                        println!("unexpected: {}", number);
                        field_value = 0.0
                    }
                }
            }
            return CSVValue::Number(field_value);
        },
        // Ignore the rest! Should not happen anyway.
        _ => return CSVValue::Number(0.0)
    }
}

fn main() {

    let mut field_sum: f64 = 0.0;
    let mut record_count: u64 = 0;

    let my_file_as_string: String = read_file_to_string("src/numbers.csv");
    let parsing_result = CSVParser::parse(Rule::file, &my_file_as_string);
    let file = parsing_result
        .expect("unsuccessful parse")
        .next()
        .unwrap();

    for pair in file.into_inner() {
        println!("record_part: {:?}", pair);
        match pair.as_rule() {
            Rule::record => {
                println!("found a record while parsing");
                record_count += 1;

                for record_internal_pair in pair.into_inner() {
                    match record_internal_pair.as_rule() {
                        Rule::field => {
                            println!("found a field INSIDE record while parsing");
                            match parse_field(record_internal_pair) {
                                CSVValue::Number(val) => field_sum += val
                            };
                        },
                        Rule::field_separator => (),
                        Rule::EOI => (),
                        // Ignore the rest, it is not going to happen!
                        _ => unreachable!()
                    }
                }
            },
            Rule::record_end => println!("found a record_end while parsing"),
            Rule::EOI => println!("found EOI while parsing"),
            _ => unreachable!()
        }
    }

    println!("Sum of fields: {}", field_sum);
    println!("Number of records: {}", record_count);
}
